package com.zc.config;

import com.zc.enums.MyEvent;
import com.zc.enums.MyState;
import lombok.SneakyThrows;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.Message;
import org.springframework.statemachine.config.EnableStateMachine;
import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
import org.springframework.statemachine.listener.StateMachineListener;
import org.springframework.statemachine.listener.StateMachineListenerAdapter;
import org.springframework.statemachine.state.State;
import org.springframework.statemachine.transition.Transition;

import java.util.EnumSet;
import java.util.Optional;

/**
 * @author : ville
 * @version : 1.0.0
 * @description : 状态机配置
 * @createTime : 2020/6/12-14:41
 * @copyright : villebo@126.com
 * @modify : ville
 **/
@Configuration
@EnableStateMachine(name = "myStateMachine")
public class StateMachineConfig extends EnumStateMachineConfigurerAdapter<MyState, MyEvent> {
    @Override
    @SneakyThrows
    public void configure(StateMachineConfigurationConfigurer<MyState, MyEvent> config) {
        config.withConfiguration().autoStartup(true).listener(myListener());
    }

    @Override
    @SneakyThrows
    public void configure(StateMachineStateConfigurer<MyState, MyEvent> states) {
        states
                .withStates()
                .initial(MyState.S1)
                .states(EnumSet.allOf(MyState.class));
    }

    @Override
    @SneakyThrows
    public void configure(StateMachineTransitionConfigurer<MyState, MyEvent> transitions) {
        transitions
                .withExternal()
                .source(MyState.S1).target(MyState.S1).event(MyEvent.E1)
                .and()
                .withExternal()
                .source(MyState.S1).target(MyState.S2).event(MyEvent.E2)
                .and()
                .withExternal()
                .source(MyState.S2).target(MyState.S3).event(MyEvent.E3);
    }

    private StateMachineListener<MyState, MyEvent> myListener() {
        return new StateMachineListenerAdapter<MyState, MyEvent>() {
            @Override
            public void transition(Transition<MyState, MyEvent> transition) {
                System.out.println("move from:{" + ofNullableState(transition.getSource()) + "} " +
                        "to:{" + ofNullableState(transition.getTarget()) + "}");
            }

            @Override
            public void stateChanged(State<MyState, MyEvent> from, State<MyState, MyEvent> to) {
                System.out.println(null != from ?
                        "State change from " + from.getId() + " to " + to.getId() : "State change" +
                        " to " + to.getId());
            }

            @Override
            public void eventNotAccepted(Message<MyEvent> event) {
                System.err.println("event not accepted: {" + event + "}");
            }

            private Object ofNullableState(State s) {
                return Optional.ofNullable(s).map(State::getId).orElse(null);
            }
        };
    }
}
